/*
 * Copyright (c) 2022 Hunan OpenValley Digital Industry Development Co., Ltd.
 *
 * HDF is dual licensed: you can use it either under the terms of
 * the GPL, or the BSD license, at your option.
 * See the LICENSE file in the root of this repository for complete details.
 */
#include <stdlib.h>
#include "adc_core.h"
#include "adc_if.h"
#include "hcs_macro.h"
#include "hdf_config_macro.h"
#include "hdf_device_desc.h"
#include "hdf_log.h"

#include "driver/adc_common.h"
#include "esp_adc_cal/include/esp_adc_cal.h"
// #include "hdf_base.h"
#define HDF_LOG_TAG "ESP32_HDF_ADC"


#define PLATFORM_CONFIG     HCS_NODE(HCS_ROOT, platform)
#define PLATFORM_ADC_CONFIG HCS_NODE(HCS_NODE(HCS_ROOT, platform), adc_config)

#define HDF_ADC_FIND_CONFIG(node, name, resource)        \
do {                                                     \
    if (strcmp(HCS_PROP(node, match_attr), name) == 0) { \
        resource->devNum = HCS_PROP(node, handle);       \
        resource->chanNum = HCS_PROP(node, channel);     \
        result = HDF_SUCCESS;                            \
    } else {                                             \
        result = HDF_FAILURE;                            \
    }                                                    \
} while (0)


static int32_t Esp32AdcRead(struct AdcDevice *device, uint32_t channel, uint32_t *val);
static int32_t Esp32AdcStart(struct AdcDevice *device);
static int32_t Esp32AdcStop(struct AdcDevice *device);

static int32_t Esp32AdcBind(struct HdfDeviceObject *device);
static int32_t Esp32AdcInit(struct HdfDeviceObject *device);
static void Esp32AdcRelease(struct HdfDeviceObject *device);

#define DEFAULT_VREF    1100        //Use adc2_vref_to_gpio() to obtain a better estimate
#define NO_OF_SAMPLES   64          //Multisampling
#define s_Delay(x) vTaskDelay(x / portTICK_RATE_MS)
static esp_adc_cal_characteristics_t *adc_chars;
static const adc_bits_width_t width = ADC_WIDTH_BIT_12;
static const adc_atten_t atten = ADC_ATTEN_11db;//ADC_ATTEN_DB_0;
adc_unit_t unit = ADC_UNIT_1;
uint32_t adc_numnber = 0;

void check_efuse(void)
{
    //Check if TP is burned into eFuse
    if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK) {
        esp_rom_printf("eFuse Two Point: Supported\n");
    } else {
        esp_rom_printf("eFuse Two Point: NOT supported\n");
    }
    //Check Vref is burned into eFuse
    if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK) {
        esp_rom_printf("eFuse Vref: Supported\n");
    } else {
        esp_rom_printf("eFuse Vref: NOT supported\n");
    }
}

void print_char_val_type(esp_adc_cal_value_t val_type)
{
    if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
        esp_rom_printf("Characterized using Two Point Value\n");
    } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
        esp_rom_printf("Characterized using eFuse Vref\n");
    } else {
        esp_rom_printf("Characterized using Default Vref\n");
    }
}

struct AdcMethod g_adcHdfMethod = {
    .start = Esp32AdcStart,
    .stop = Esp32AdcStop,
    .read = Esp32AdcRead,
};

struct HdfDriverEntry g_adcHdfDriverEntry = {
    .moduleVersion = 1,
    .moduleName = "ESP32_HDF_ADC",
    .Bind = Esp32AdcBind,
    .Init = Esp32AdcInit,
    .Release = Esp32AdcRelease,
};
HDF_INIT(g_adcHdfDriverEntry);

static int32_t Esp32AdcBind(struct HdfDeviceObject *device)
{
    (void *)device;
    return HDF_SUCCESS;
}

static int32_t Esp32AdcInit(struct HdfDeviceObject *device)
{
    HDF_LOGE("*******************************\r\n");
    
    if (device == NULL) {
        HDF_LOGE("Esp32AdcInit param is NULL!\r\n");
        return HDF_ERR_INVALID_PARAM;
    }

    int32_t result = HDF_FAILURE;
    struct AdcDevice *host = NULL;
    host = (struct AdcDevice *)OsalMemAlloc(sizeof(struct AdcDevice));
    if (host == NULL) {
        HDF_LOGE("[%s]: malloc host is NULL\r\n", __func__);
        return HDF_ERR_MALLOC_FAIL;
    }

    memset_s(host, sizeof(struct AdcDevice), 0, sizeof(struct AdcDevice));
    HCS_FOREACH_CHILD_VARGS(PLATFORM_ADC_CONFIG, HDF_ADC_FIND_CONFIG, device->deviceMatchAttr, host);
    HDF_LOGE("host.devNum=%d,host.chanNum=%d\r\n",host->devNum,host->chanNum);
    if (result != HDF_SUCCESS) {
        HDF_LOGE("Esp32 adc HCS_FOREACH_CHILD_VARGS fail\r\n");
        return HDF_FAILURE;
    }
    host->ops = &g_adcHdfMethod;
    device->priv = (void *)host;

    if(AdcDeviceAdd(host) != HDF_SUCCESS) {
        HDF_LOGE("Esp32 AdcDeviceAdd fail\r\n");
        return HDF_FAILURE;     
    }

    return HDF_SUCCESS;
}

static void Esp32AdcRelease(struct HdfDeviceObject *device)
{
    AdcDeviceRemove((struct AdcDevice*)device->priv);
    OsalMemFree(device->priv);
    device->priv = NULL;
}

int32_t AdcDevOpen(uint32_t number)
{

    adc_numnber = number;
    HDF_LOGE("%s adc_numnber = %d\r\n", __func__, adc_numnber);
    if(number == 0 || number > 2)
    {
        esp_rom_printf("\n AdcDevOpen param input error!\n");
        return ESP_FAIL;
    }  
    //Check if Two Point or Vref are burned into eFuse
    // check_efuse();
    if(number == 2){
        unit = ADC_UNIT_2;
    }
    else{
        unit = ADC_UNIT_1;
    }
    adc_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t));
    esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, width, DEFAULT_VREF, adc_chars);//在特定衰减下表征ADC>的特性

    return HDF_SUCCESS;
}

void AdcDevClose(uint32_t devNum)
{
    return ;
}

int32_t AdcDevRead(void *handle, uint32_t devNum, uint32_t channel, uint32_t *val)
{
    if(adc_numnber <= 0 || adc_numnber > 2 || channel > 9 || channel < 0)
    {
        esp_rom_printf("\n Please call AdcDevOpen function first!\n");
        return ESP_FAIL;
    }  
    check_efuse();
    if(adc_numnber == 2){
        unit = ADC_UNIT_2;
    }
    else{
        unit = ADC_UNIT_1;
    }
    //Configure ADC
    if (unit == ADC_UNIT_1) {//adc1/2选择配置
        adc1_config_width(width);;//采集宽度
        adc1_config_channel_atten(channel, atten);//配置通道以及衰减度
        // HDF_LOGE("%s adc_numnber = %d\r\n", __func__, adc_numnber);
    } else {
        adc2_config_channel_atten((adc2_channel_t)channel, atten);
    }
    //Continuously sample ADC1
    uint32_t adc_reading = 0;
    //Multisampling
    for (int i = 0; i < NO_OF_SAMPLES; i++) {
        if (unit == ADC_UNIT_1) {
            adc_reading += adc1_get_raw((adc1_channel_t)channel);//从单个通道获取ADC1读数
            // HDF_LOGE("%s adc1_get_raw((adc1_channel_t)channel) = %d\r\n", __func__, adc1_get_raw((adc1_channel_t)channel));
        } else {
            int raw;
            adc2_get_raw((adc2_channel_t)channel, width, &raw);
            adc_reading += raw;
        }
    }
    // HDF_LOGE("%s adc_reading = %d\r\n", __func__, adc_reading);
    adc_reading /= NO_OF_SAMPLES;

    HDF_LOGE("%s adc_reading111 = %d\r\n", __func__, adc_reading);
    //Convert adc_reading to voltage in mV
    uint32_t voltage = esp_adc_cal_raw_to_voltage(adc_reading, adc_chars);//将ADC读数转换为以mV为单位的电压
    if(voltage != 0){
        *val = voltage;
    }  
    return HDF_SUCCESS;
}

static int32_t Esp32AdcRead(struct AdcDevice *device, uint32_t channel, uint32_t *val)
{
    return (AdcDevRead(NULL, device->devNum, channel, val) == HDF_SUCCESS) ? HDF_SUCCESS : HDF_FAILURE;
}

static int32_t Esp32AdcStart(struct AdcDevice *device)
{
    return (AdcDevOpen(device->devNum) == HDF_SUCCESS) ? HDF_SUCCESS : HDF_FAILURE;
}

static int32_t Esp32AdcStop(struct AdcDevice *device)
{
    AdcDevClose(device->devNum);
    return HDF_SUCCESS;
}
